﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls;


namespace Chisel.Core
{
    public class Entity : Actor
    {
        public Entity()
        {
			Width = 150;
			Height = 200;

			_border = new Border() {
				BorderBrush = new SolidColorBrush(Colors.Black),
				BorderThickness = new Thickness(1),
				Background = new SolidColorBrush(Colors.White)
			};
			
			Content = _border;
			_mainStack = new StackPanel() {
				Orientation = Orientation.Vertical
			};
			_border.Child = _mainStack;

			// create the title
			_titleStack = new StackPanel() {
				Orientation = Orientation.Horizontal,
			};
			_mainStack.Children.Add(_titleStack);
			_titleBorder = new Border() {
				Padding = new Thickness(4)
			}
			;
			_titleStack.Children.Add(_titleBorder);
			_title = new TextBlock() {
			};
			
			_titleBorder.Child = _title;

			Title = "Title";
        }

		private Border _border;

		private StackPanel _mainStack;


		#region Title

		private StackPanel _titleStack;

		private Border _titleBorder;

		private TextBlock _title;
        /// <summary>
        /// The title of the entity.
        /// </summary>
        public string Title
        {
            set { _title.Text = value; }
			get { return _title.Text; }
		}

		/// <summary>
		/// This gets called whenever the user wants to change the title.
		/// </summary>
		public event TextChangedValidator TitleChanged;

		/// <summary>
		/// Gives the user a chance to edit the title.
		/// </summary>
		public void EditTitle()
		{
			var pos = new Point(Position.X + _title.Padding.Left, Position.Y + _title.Padding.Top);
			var titleSize = new Point(_titleStack.ActualWidth - _title.Padding.Left,
				_titleStack.ActualHeight - _title.Padding.Top);
			ParentCanvas.EditText(pos, titleSize, Title, delegate(string val)
			{
				EndEditTitle(val);
			});
		}

		/// <summary>
		/// Handles the end of an editing action of the title.
		/// </summary>
		protected virtual void EndEditTitle(string val)
		{
			bool valid = TitleChanged == null || TitleChanged(this, val);
			if (valid)
				Title = val;
			else
				EditTitle();
		}

		#endregion


        public override Point Center
        {
            get {return new Point(Position.X + Width / 2, Position.Y + Height / 2);}
		}

		protected override void OnSizeChanged(object sender, SizeChangedEventArgs e)
		{
			base.OnSizeChanged(sender, e);

			_titleBorder.Width = Width;
		}

        /// <summary>
        /// Resets the style based on the current hit state and the canvas style provider.
        /// </summary>
        protected override void ResetStyle()
        {
            var foreBrush = GetBrush(BrushType.Foreground, State);
            var foreColor = (foreBrush as SolidColorBrush).Color;
            _titleBorder.Background = new LinearGradientBrush() {
                GradientStops = new GradientStopCollection() {
                    new GradientStop() {Color = foreColor, Offset = 0},
                    new GradientStop() {Color = Colors.White, Offset = 1}
                },
                MappingMode = BrushMappingMode.RelativeToBoundingBox,
                StartPoint = new Point(0, 0),
                EndPoint = new Point(1, 0),
                Opacity = 0.15
            };
            _border.BorderBrush = foreBrush;

			if (ParentCanvas.StyleProvider.EntityRadius != _border.CornerRadius.BottomLeft)
			{
				_border.CornerRadius = new CornerRadius(ParentCanvas.StyleProvider.EntityRadius);
				_titleBorder.CornerRadius = new CornerRadius(ParentCanvas.StyleProvider.EntityRadius, ParentCanvas.StyleProvider.EntityRadius, 0, 0);
			}

			_title.FontSize = StyleFontSize + 2;
		}


		#region Selection

		protected override void SelectAction()
		{
			base.SelectAction();

			Children.DeselectAll();
		}

		#endregion


		#region Mouse Interaction

		protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
		{
			if (e.Handled)
				return;

			base.OnMouseLeftButtonDown(e);

            // see if we should start dragging
            var titlePoint = e.GetPosition(_title);
            if (titlePoint.X >= 0 && titlePoint.X < ActualWidth &&
                    titlePoint.Y >= 0 && titlePoint.Y < _title.ActualHeight + _title.Padding.Top + _title.Padding.Bottom)
            {
                ParentCanvas.Actors.BeginDrag(e);
                e.Handled = true;
			}
        }

        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
			base.OnMouseLeftButtonUp(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
        }

		protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
		{
			base.OnMouseDoubleClick(e);

			var titlePoint = e.GetPosition(_title);
			if (titlePoint.X >= 0 && titlePoint.X < ActualWidth &&
					titlePoint.Y >= 0 && titlePoint.Y < _title.ActualHeight + _title.Padding.Top + _title.Padding.Bottom)
			{
				e.Handled = true;
				EditTitle();
			}
		}

        /// <summary>
        /// The anchor point for dragging operations.
        /// </summary>
		private Point _anchor;

		public override void OnDeselect()
		{
			base.OnDeselect();
			Children.DeselectAll();
			foreach (var child in Children.All)
			{
				if (child is ItemGroup)
					(child as ItemGroup).Items.DeselectAll();
			}
		}


        public override void OnBeginDrag(MouseButtonEventArgs e)
        {
			base.OnBeginDrag(e);
            _anchor = e.GetPosition(this);
        }

		public override void OnDrag(MouseEventArgs e)
        {
			base.OnDrag(e);
            if (State.IsDragging)
            {
                var pos = e.GetPosition(ParentCanvas);
                Position = new Point(pos.X - _anchor.X, pos.Y - _anchor.Y);
				if (ParentCanvas != null)
					ParentCanvas.ValidateActorPosition(this);
            }
        }


        #endregion


        #region Items

        /// <summary>
        /// The collection of actors in the entity.
        /// </summary>
        /// <remarks>Also stores the selected items.</remarks>
        public ActorCollection<EntityChild> Children = new ActorCollection<EntityChild>();
        
        /// <summary>
        /// Adds an item to the entity.
        /// </summary>
        public void AddChild(EntityChild child)
        {
            Children.Add(child);
            _mainStack.Children.Add(child);
            child.Entity = this;
        }

        /// <summary>
        /// Removes an item from the entity.
        /// </summary>
        public void RemoveChild(EntityChild child)
        {
            Children.Remove(child);
            _mainStack.Children.Remove(child);
        }

        #endregion


		#region Connections

		public override Point GetConnectionPoint(Point point, out Point? dir)
		{
			Point? intersection;

			double width = Width;
			double height = Height;
			double edge = ParentCanvas.StyleProvider.EntityRadius * 2;

			intersection = Intersect(Position, new Point(Position.X, Position.Y + height), Center, point);
			if (intersection != null)
			{
				dir = new Point(-1, 0);
				Point val = intersection.Value;
				val.Y = Math.Min(Position.Y + height - edge, val.Y);
				val.Y = Math.Max(Position.Y + edge, val.Y);
				return val;
			}

			intersection = Intersect(Position, new Point(Position.X + width, Position.Y), Center, point);
			if (intersection != null)
			{
				dir = new Point(0, 1);
				Point val = intersection.Value;
				val.X = Math.Min(Position.X + width - edge, val.X);
				val.X = Math.Max(Position.X + edge, val.X);
				return val;
			}

			intersection = Intersect(new Point(Position.X, Position.Y + height),
				new Point(Position.X + width, Position.Y + height), Center, point);
			if (intersection != null)
			{
				dir = new Point(0, -1);
				Point val = intersection.Value;
				val.X = Math.Min(Position.X + width - edge, val.X);
				val.X = Math.Max(Position.X + edge, val.X);
				return val;
			}

			intersection = Intersect(new Point(Position.X + width, Position.Y),
				new Point(Position.X + width, Position.Y + height), Center, point);
			if (intersection != null)
			{
				dir = new Point(1, 0);
				Point val = intersection.Value;
				val.Y = Math.Min(Position.Y + height - edge, val.Y);
				val.Y = Math.Max(Position.Y + edge, val.Y);
				return val;
			}

			dir = new Point(0, 0);
			return new Point(0, 0);
		}

		#endregion

	}
}
